<template>
    <div class="borderBox" v-for="item, index in inputData" :key="item.titel">
        <div class="fromText">{{ item.titel }}</div>
        <textarea :rows="item.row ? item.row : 3" :class="item.onerror ? 'errInput' : ''" v-model="item.input"
            v-if="item.inputType === 'textarea'" @blur="inputBlur(index)"></textarea>
        <input v-else class="fromInpt" :class="item.onerror ? 'errInput' : ''" v-model="item.input"
            @blur="inputBlur(index)" :type='item.inputType ? item.inputType : "text"' :placeholder="item.defult" />
        <div class="errRoules" v-if="item.onerror">
            {{ item.errmesage }}
        </div>
    </div>
    <div @click="sumbitClick" class="sumbit">
        <slot name="sumbit">
            <div class="btn btnStyle" v-if="sumbit">
                {{ sumbit }}
            </div>
        </slot>
    </div>
</template>

<script lang='ts'>
import { reactive, PropType, defineComponent } from 'vue'
interface RoulsType {
    type: 'required' | 'mail' | 'phone' | 'password',
    text: string
}
interface FromType {
    titel: string
    valueKey: string
    inputType?: 'text' | 'password' | 'checkbox' | 'textarea',
    row?: number
    rules?: Array<RoulsType>
    defult?: string
    custom?: (value: string) => string | false
    affirm?: {
        target: Array<string>
        text: string
    }
}
interface InputType { input: string, onerror: boolean, errmesage: string }
type AddType = InputType & FromType
type InputData = Array<AddType>
type useFromType = () => {
    finish: () => boolean | { [name: string]: string }
    clearAll: () => void
    setValue: (value: setVaueType) => void
}
type setVaueType = { [name: string]: string }

export type FromProps = Array<FromType>

export const useFrom: useFromType = () => ({
    finish: () => false,
    clearAll: () => null,
    setValue: () => null
})

export default defineComponent({
    name: 'From',
    props: {
        fromData: {
            type: Array as PropType<FromProps>,
            required: true
        },
        from: Object as PropType<ReturnType<useFromType>>,
        sumbit: String,
        beforeUpload: Function
    },
    emits: ['onSumbit'],
    setup(props, { emit }) {
        const { fromData, from } = props
        const inputData = reactive<InputData>(fromData.map(item => {
            return {
                ...item, input: '', onerror: false, errmesage: ''
            }
        }))
        const inputBlur = (index: number) => {
            const { rules, input, custom, affirm } = inputData[index]
            if (rules && rules.length > 0 || custom) {
                if (rules) {
                    inputData[index].onerror = rules.some(item => {
                        let showErr = inputData[index].onerror
                        inputData[index].errmesage = item.text
                        switch (item.type) {
                            case 'required':
                                if (!input.trim()) {
                                    showErr = true
                                } else {
                                    showErr = false
                                }
                                break
                            case 'mail':
                                const regMail = /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/;
                                showErr = !regMail.test(input)
                                break
                            case "phone":
                                const regPhone = /^1[3456789]\d{9}$/
                                showErr = !regPhone.test(input)
                                break
                            case 'password':
                                const regPassword = /^.*(?=.{6,})(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*? ]).*$/;
                                showErr = !regPassword.test(input)
                                break
                        }
                        return showErr
                    })
                    if (inputData[index].onerror) return
                }
                if (custom) {
                    const result = custom(input)
                    if (result) {
                        inputData[index].onerror = true
                        inputData[index].errmesage = result
                        return
                    }
                }
                if (affirm) {
                    const { text, target } = affirm
                    inputData.forEach(item => {
                        const result = target.indexOf(item.valueKey) !== -1
                        if (result && !inputData[index].onerror) {
                            if (item.input !== input) {
                                inputData[index].onerror = true
                                inputData[index].errmesage = text
                            }
                        }
                    })
                }
            }
        }
        const useComplete = () => {
            const finish = inputData.reduce<boolean>((value, item, index) => {
                inputBlur(index)
                if (item.onerror) {
                    return false
                }
                return value
            }, true)
            if (finish) {
                return inputData.reduce<{ [name: string]: string }>((value, item) => {
                    value[item.valueKey] = item.input
                    return value
                }, {})
            }
            return false
        }
        const useClearValue = () => {
            inputData.forEach(item => {
                item.input = ''
                item.onerror = false
            })
        }
        const sumbitClick = () => {
            emit('onSumbit', useComplete())
        }
        const useSetValue = (value: setVaueType) => {
            const keys = Object.keys(value)
            keys.forEach(item => {
                const index = inputData.findIndex(input => input.valueKey === item)
                if (index !== -1) {
                    inputData[index].input = value[item]
                }
            })
            inputData.forEach(item => {
                item.onerror = false
            })
        }
        if (from) {
            from.finish = useComplete
            from.clearAll = useClearValue
            from.setValue = useSetValue
        }
        return { inputData, inputBlur, useComplete, sumbitClick }
    }
})
</script>
<style scoped lang="less">
.borderBox {
    width: 100%;
    display: flex;
    position: relative;
    flex-wrap: wrap;

    textarea {
        width: 100%;
        border: 2px solid #0d6efd;
        border-radius: 5px;
        outline: none;
    }
}

.fromText {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: left;
    padding-right: 10px;
    align-items: center;
}

.fromInpt {
    width: 100%;
    border: 2px solid #0d6efd;
    border-radius: 5px;
    outline: none;
}

.sumbit {
    display: flex;
    margin-top: 10px;
    justify-content: center;
}

.errRoules {
    position: absolute;
    top: 5px;
    left: 50%;
    color: red;
    font-size: 15px;
    transform: translateX(-50%);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.errInput {
    border: 2px solid red !important;
}

.btnStyle {
    border: #0d6efd 2px solid;
    background-color: #0d6efd;
    color: white;
    padding: 0px 10px;
}
</style>